home *** CD-ROM | disk | FTP | other *** search
/ c't freeware shareware 1997 / CT_SW_97.ISO / pc / software / entwickl / macos / waste13a.hqx / WASTE 1.3a3 Distribution / WASTE 1.3a3 / WELowLevelEditing.c < prev    next >
Text File  |  1996-12-30  |  27KB  |  964 lines

  1. /*
  2.  *    WELowLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Low-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1997 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal Boolean _WEIsWordRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  18. {
  19.     // _WEIsWordRange returns true if the specified range is a word range,
  20.     // i.e. if it would be possible to select it by double-clicking and (optionally) dragging.
  21.  
  22.     SInt32 wordStart, wordEnd;
  23.  
  24.     // determine if rangeStart is at the beginning of a word
  25.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  26.     if (rangeStart == wordStart)
  27.     {
  28.  
  29.         // determine if rangeEnd is at the end of a word
  30.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  31.         return (rangeEnd == wordEnd);
  32.     }
  33.     return false;
  34. }
  35.  
  36. pascal Boolean _WEIsPunct(SInt32 offset, WEHandle hWE)
  37. {
  38.     SInt16 cType;
  39.  
  40.     cType = WECharType(offset, hWE);
  41.     if ((cType & smcTypeMask) == smCharPunct)
  42.     {
  43.         cType &= smcClassMask;
  44.         if ((cType == smPunctNormal) || (cType == smPunctBlank))
  45.             return true;
  46.     }
  47.     return false;
  48. }
  49.  
  50. pascal void _WEIntelligentCut(SInt32 *rangeStart, SInt32 *rangeEnd, WEHandle hWE)
  51. {
  52.  
  53.     // _WEIntelligentCut is called by other WASTE routines to determine the actual
  54.     // range to be deleted when weFIntCutAndPaste is enabled.
  55.     // On entry, rangeStart and rangeEnd specify the selection range visible to the user.
  56.     // On exit, rangeStart and rangeEnd specify the actual range to be removed.
  57.  
  58.     // do nothing if the intelligent cut-and-paste feature is disabled
  59.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  60.         return;
  61.  
  62.     // intelling cut-&-paste rules should be applied only to word ranges...
  63.     if (!_WEIsWordRange(*rangeStart, *rangeEnd, hWE))
  64.         return;
  65.  
  66.     // ...without punctuation characters at the beginning or end
  67.     if (_WEIsPunct(*rangeStart, hWE))
  68.         return;
  69.     if (_WEIsPunct(*rangeEnd - 1, hWE))
  70.         return;
  71.  
  72.     // if the character preceding the selection range is a space, discard it
  73.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace)
  74.         (*rangeStart)--;
  75.     // else, if the character following the selection range is a space, discard it
  76.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  77.         (*rangeEnd)++;
  78.  
  79. }
  80.  
  81. pascal SInt16 _WEIntelligentPaste(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  82. {
  83.     SInt16 retval;
  84.  
  85.     // _WEIntelligentPaste is called by other WASTE routines to determine whether
  86.     // an additional space character should be added (before or after) after inserting
  87.     // new text (usually from the Clipboard or from a drag).
  88.  
  89.     retval = weDontAddSpaces;
  90.  
  91.     // do nothing unless the intelligent cut-and-paste feature is enabled
  92.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  93.         return retval;
  94.  
  95.     // extra spaces will be added only if the pasted text looks like a word range,
  96.     // without punctuation characters at the beginning or at the end
  97.     if (_WEIsPunct(rangeStart, hWE))
  98.         return retval;
  99.     if (_WEIsPunct(rangeEnd - 1, hWE))
  100.         return retval;
  101.  
  102.     // if the character on the left of the pasted text is a punctuation character
  103.     // and the character on the right isn't,  add a space on the right, and vice versa
  104.     if (_WEIsPunct(rangeStart - 1, hWE))
  105.     {
  106.         if (!_WEIsPunct(rangeEnd, hWE))
  107.         {
  108.             retval = weAddSpaceOnRightSide;
  109.         }
  110.     }
  111.     else if (_WEIsPunct(rangeEnd, hWE))
  112.     {
  113.         retval = weAddSpaceOnLeftSide;
  114.     }
  115.  
  116.     return retval;
  117. }
  118.  
  119. pascal OSErr _WEInsertRun(SInt32 runIndex, SInt32 offset, SInt32 styleIndex, WEPtr pWE)
  120. {
  121.  
  122.     // Insert a new element in the style run array, at the specified runIndex position.
  123.     // The new element consists of the pair <offset, styleIndex>.
  124.  
  125.     RunArrayElement element;
  126.     OSErr err;
  127.  
  128.     // prepare the element record to be inserted in the array
  129.     element.runStart = offset;
  130.     element.styleIndex = styleIndex;
  131.  
  132.     // do the insertion
  133.     if ((err = _WESplice((Handle) pWE->hRuns, &element, sizeof(element), (runIndex + 1) * sizeof(element))) != noErr)
  134.         return err;
  135.  
  136.     // increment style run count
  137.     pWE->nRuns++;
  138.  
  139.     // increment the reference count field of the style table element
  140.     // referenced by the newly inserted style run
  141.     (*pWE->hStyles)[styleIndex].refCount++;
  142.  
  143.     return noErr;
  144. }
  145.  
  146. pascal void _WERemoveRun(SInt32 runIndex, WEPtr pWE)
  147. {
  148.     StyleTableElementPtr pStyle;
  149.  
  150.     // get a pointer to the style table element referenced by the style run
  151.     pStyle = *pWE->hStyles + (*pWE->hRuns)[runIndex].styleIndex;
  152.  
  153.     // decrement the reference count field of the style table element
  154.     // referenced by the style run to be removed
  155.     pStyle->refCount--;
  156.  
  157. #if WASTE_OBJECTS
  158.     // dispose of the embedded object (if any)
  159.     if (pStyle->info.runStyle.tsObject != nil)
  160.         _WEFreeObject(pStyle->info.runStyle.tsObject);
  161. #endif
  162.  
  163.     // remove a "slot" from the run array
  164.     _WESplice((Handle) pWE->hRuns, nil, - sizeof(RunArrayElement), runIndex * sizeof(RunArrayElement));
  165.  
  166.     // decrement style run count
  167.     pWE->nRuns--;
  168. }
  169.  
  170. pascal void _WEChangeRun(SInt32 runIndex, SInt32 newStyleIndex, Boolean keepOld, WEPtr pWE)
  171. {
  172.     // change the styleIndex field of the specified element of the style run array
  173.  
  174.     SInt32 oldStyleIndex;
  175.     StyleTableElementPtr oldStyle, newStyle;
  176.  
  177.     // do the change
  178.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  179.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  180.  
  181.     // get pointers to old and new style table elements
  182.     oldStyle = *pWE->hStyles + oldStyleIndex;
  183.     newStyle = *pWE->hStyles + newStyleIndex;
  184.  
  185.     // increment the reference count field of the new style table element
  186.     newStyle->refCount++;
  187.  
  188.     // decrement the reference count field of the old style table element
  189.     oldStyle->refCount--;
  190.  
  191. #if WASTE_OBJECTS
  192.     // dispose of the embedded object (if any) unless it is again referenced in the new style
  193.     if (!keepOld)
  194.     {
  195.         WEObjectDescHandle oldObject = oldStyle->info.runStyle.tsObject;
  196.  
  197.         if ((oldObject != nil) && (oldObject != newStyle->info.runStyle.tsObject))
  198.             _WEFreeObject(oldObject);
  199.     }
  200. #endif
  201.  
  202. }
  203.  
  204. pascal OSErr _WENewStyle(const WETextStyle *ts, SInt32 *styleIndex, WEPtr pWE)
  205. {
  206.     // given the specified WETextStyle record, find the corresponding entry
  207.     // in the style table (create a new entry if necessary), and return its index
  208.  
  209.     StyleTableElementPtr pElement;
  210.     StyleTableElement element;
  211.     SInt32 index;
  212.     SInt32 unusedIndex;
  213.     OSErr err;
  214.  
  215.     // see if the given style already exists in the style table
  216.     // while scanning the table, also remember the position of the first unused style, if any
  217.     unusedIndex = -1;
  218.     pElement = *pWE->hStyles;
  219.     for ( index = 0 ; index < pWE->nStyles ; index++ )
  220.     {
  221.         // check for entries which aren't referenced and can be recycled
  222.         if (pElement->refCount == 0)
  223.             unusedIndex = index;
  224.  
  225.         // perform a bitwise comparison between the current element and the specified style
  226.         // (actually, we ignore metrics information)
  227.         else if (_WEBlockCmp(&pElement->info.runStyle, ts, sizeof(WETextStyle)))
  228.         {
  229.             // found: style already present
  230.             *styleIndex = index;
  231.             return noErr;
  232.         }
  233.         pElement++;
  234.     } // for
  235.  
  236.     // the specified style doesn't exist in the style table
  237.     // since this is a new entry, we have to calculate font metrics information
  238.     element.info.runStyle = *ts;
  239.     _WEFillFontInfo(pWE->port, &element.info);
  240. /*
  241.     // set the high bit of tsFlags if attributes specify a right-to-left run
  242.     if (BTST(pWE->flags, weFBidirectional))
  243.     {
  244.         if (GetScriptVariable(FontToScript(element.info.runStyle.tsFont), smScriptRight) != 0)
  245.         {
  246.             BSET(element.info.runStyle.tsFlags, tsRightToLeft);
  247.         }
  248.     }
  249. */
  250.     // see if we can recycle an unused entry
  251.     if (unusedIndex >= 0)
  252.     {
  253.         index = unusedIndex;
  254.         (*pWE->hStyles)[index].info = element.info;
  255.     }
  256.     else
  257.     {
  258.         // no reusable entry: we have to append a new element at the end of the table
  259.         element.refCount = 0;
  260.         if ((err = _WESplice((Handle) pWE->hStyles, &element, sizeof(element), -1)) != noErr)
  261.             return err;
  262.  
  263.         // update style count in the WE record
  264.         pWE->nStyles++;
  265.     }
  266.  
  267.     // return the index to the new element
  268.     *styleIndex = index;
  269.     return noErr;
  270. }
  271.  
  272. pascal OSErr _WERedraw(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  273. {
  274.     WEPtr pWE = *hWE;        // assume WE record is already locked
  275.     LineArrayPtr pLines;
  276.     SInt32 startLine, endLine;
  277.     SInt32 oldTextHeight, newTextHeight;
  278.     LongRect r;
  279.     Rect viewRect, updateRect;
  280.     RgnHandle saveClip;
  281.     GrafPtr savePort;
  282.     OSErr err;
  283. #if WASTE_REDRAW_SPEED
  284.     LongRect scrollRect;
  285.     RgnHandle updateRgn,
  286.               utilRgn;
  287. #endif
  288.  
  289.     // do nothing if recalculation has been inhibited
  290.     if (!BTST(pWE->features, weFInhibitRecal))
  291.     {
  292.         // hide the caret
  293. #if WASTE_REDRAW_SPEED
  294.         BCLR(pWE->flags, weFCaretVisible);
  295. #else
  296.         if (BTST(pWE->flags, weFCaretVisible))
  297.         {
  298.             _WEBlinkCaret(hWE);
  299.         }
  300. #endif
  301.  
  302.         // remember total text height
  303.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  304.  
  305.         // find line range affected by modification
  306.         startLine = WEOffsetToLine(rangeStart, hWE);
  307.         endLine = WEOffsetToLine(rangeEnd, hWE);
  308.  
  309.         // recalculate line breaks starting from startLine
  310.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) != noErr)
  311.         {
  312.             goto cleanup;
  313.         }
  314.  
  315.         // recalculate slops
  316.         _WERecalSlops(startLine, endLine, hWE);
  317.  
  318.         // do nothing if redrawing has been inhibited
  319.         if (!BTST(pWE->features, weFInhibitRedraw))
  320.         {
  321.  
  322.             // calculate new total text height
  323.             newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  324.  
  325.             // calculate the rectangle to redraw (in long coordinates)
  326.             r.left = -SHRT_MAX;
  327.             r.right = SHRT_MAX;
  328.             pLines = *pWE->hLines;
  329.             r.top = pLines[startLine].lineOrigin;
  330.  
  331. #if WASTE_REDRAW_SPEED
  332.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  333.             // otherwise we must redraw all lines from startLine on
  334.  
  335.             if (endLine < pWE->nLines - 1)
  336.                  r.bottom = pLines[endLine + 1].lineOrigin;
  337.             else
  338.                 r.bottom = newTextHeight;
  339.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  340.  
  341.             if (newTextHeight == oldTextHeight)
  342.                 WELongRectToRect(&r, &updateRect);
  343.             else
  344.             {
  345.                 /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  346.                  *      use scroll bits to move the displayed text up.
  347.                  */
  348.  
  349.                 scrollRect = pWE->viewRect;
  350.                 if (newTextHeight > oldTextHeight)
  351.                     scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  352.                 else
  353.                     scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  354.                 WELongRectToRect(&scrollRect, &updateRect);
  355.                 updateRgn = NewRgn();
  356.                 ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  357.  
  358.                 //      Redraw the exposed region (caused by a scroll up)
  359.  
  360.                 WELongRectToRect(&r, &updateRect);
  361.                 utilRgn = NewRgn();
  362.                 RectRgn(utilRgn, &updateRect);
  363.                 DiffRgn(updateRgn, utilRgn, updateRgn);
  364.                 DisposeRgn(utilRgn);
  365.                 WEUpdate(updateRgn, hWE);
  366.                 DisposeRgn(updateRgn);
  367.             }
  368. #else
  369.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  370.             // otherwise we must redraw all lines from startLine on
  371.             if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1))
  372.             {
  373.                 r.bottom = pLines[endLine + 1].lineOrigin;
  374.             }
  375.             else if (newTextHeight < oldTextHeight)
  376.             {
  377.                 r.bottom = oldTextHeight;
  378.             }
  379.             else
  380.             {
  381.                 r.bottom = newTextHeight;
  382.             }
  383.  
  384.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  385.  
  386.             // calculate the intersection between this rectangle and the view rectangle
  387.             WELongRectToRect(&r, &updateRect);
  388. #endif
  389.             WELongRectToRect(&pWE->viewRect, &viewRect);
  390.  
  391.             if (SectRect(&updateRect, &viewRect, &updateRect))
  392.             {
  393.                 // set up the port and the clip region
  394.                 GetPort(&savePort);
  395.                 SetPort(pWE->port);
  396.  
  397.                 // set the clip region to updateRect
  398.                 saveClip = NewRgn();
  399.                 GetClip(saveClip);
  400.                 ClipRect(&updateRect);
  401.  
  402.                 // we only really need to redraw the visible lines
  403.                 startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  404.                 endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  405.  
  406.                 // redraw the lines (pass true in the doErase parameter)
  407.                 _WEDrawLines(startLine, endLine, true, hWE);
  408.  
  409.                 // erase the portion of the update rectangle below the last line (if any)
  410.                 pLines = *pWE->hLines;
  411.                 updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  412.                 if (updateRect.top < updateRect.bottom)
  413.                 {
  414.                     CallWEEraseProc(&updateRect, hWE, pWE->eraseHook);
  415.                 }
  416.  
  417.                 // restore the clip region
  418.                 SetClip(saveClip);
  419.                 DisposeRgn(saveClip);
  420.  
  421.                 // restore the port
  422.                 SetPort(savePort);
  423.  
  424.                 // redraw the caret or the selection range
  425.                 if (pWE->selStart < pWE->selEnd)
  426.                 {
  427.                     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  428.                 }
  429.                 else
  430.                 {
  431.                     _WEBlinkCaret(hWE);
  432.                 }
  433.             } // if SectRect
  434.  
  435.             // scroll the selection range into view
  436.             WESelView(hWE);
  437.  
  438.         } // if redraw not inhibited
  439.     } // if recal not inhibited
  440.  
  441.     // clear result code
  442.     err = noErr;
  443.  
  444. cleanup:
  445.     // return result code
  446.     return err;
  447. }
  448.  
  449. pascal OSErr WECalText(WEHandle hWE)
  450. {
  451.     Boolean saveWELock;
  452.     OSErr err;
  453.  
  454.     // lock WE record
  455.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  456.  
  457. #if WASTE_WECALTEXT_DOES_REDRAW
  458.  
  459.     // recalculate line breaks & slops and redraw the text
  460.     err = _WERedraw(0, LONG_MAX, hWE);
  461.  
  462. #else
  463.  
  464.     {
  465.         SInt32 startLine = 0;
  466.         SInt32 endLine = LONG_MAX;
  467.  
  468.         // recalculate line breaks & slops without redrawing anything
  469.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) == noErr)
  470.             _WERecalSlops(startLine, endLine, hWE);
  471.     }
  472.  
  473. #endif
  474.  
  475.     // unlock the WE record
  476.     _WESetHandleLock((Handle) hWE, saveWELock);
  477.  
  478.     // return result code
  479.     return err;
  480. }
  481.  
  482. pascal OSErr _WESetStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode mode, const WETextStyle *ts, WEHandle hWE)
  483. {
  484.     // alter the style attributes of the specified text range according to ts and mode
  485.  
  486.     WEPtr pWE = *hWE;                    // assume WE record is already locked
  487.     RunArrayHandle hRuns = pWE->hRuns;
  488.     SInt32 offset;
  489.     SInt32 runIndex;
  490.     SInt32 oldStyleIndex, newStyleIndex;
  491.     WERunInfo runInfo;
  492.     Style continuousStyles;
  493.     OSErr err;
  494.  
  495.     WEASSERT(((rangeStart < rangeEnd) || ((rangeStart == 0) && (rangeEnd == 0) && (pWE->textLength == 0))), "\pBad style range");
  496.  
  497.     // if mode contains weDoToggleFace, we need to determine which Quickdraw styles
  498.     // are continuous over the specified text range: those styles must be turned off
  499.     if (BTST(mode, kModeToggleFace))
  500.     {
  501.         WEStyleMode temp = weDoFace;
  502.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  503.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  504.     }
  505.     else
  506.     {
  507.         continuousStyles = normal;
  508.     }
  509.  
  510.     // find the index to the first style run in the specified range
  511.     offset = rangeStart;
  512.     runIndex = WEOffsetToRun(offset, hWE);
  513.  
  514.     // run thru all the style runs that encompass the selection range
  515.     do
  516.     {
  517.         // find style index for this run and retrieve corresponding style attributes
  518.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  519.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  520.  
  521.         // _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run:
  522.         // correct this anomaly (which is useful for other purposes, anyway)
  523.         if (runInfo.runEnd > pWE->textLength)
  524.         {
  525.             runInfo.runEnd = pWE->textLength;
  526.         }
  527.  
  528.         // apply changes to existing style attributes as requested
  529.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  530.  
  531.         // the high bit of tsFlags must be set if the font
  532.         // belongs to a right-to-left script system
  533.         if (BTST(pWE->flags, weFBidirectional))
  534.         {
  535.             if (GetScriptVariable(FontToScript(runInfo.runAttrs.runStyle.tsFont), smScriptRight) != 0)
  536.             {
  537.                 BSET(runInfo.runAttrs.runStyle.tsFlags, tsRightToLeft);
  538.             }
  539.         }
  540.  
  541.         // get a style index for the new text style
  542.         if ((err = _WENewStyle(&runInfo.runAttrs.runStyle, &newStyleIndex, pWE)) != noErr)
  543.             goto cleanup;
  544.  
  545.         // if offset falls on a style boundary and this style run has become identical
  546.         // to the previous one, merge the two runs together
  547.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  548.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  549.         {
  550.             _WERemoveRun(runIndex, pWE);
  551.             runIndex--;
  552.         }
  553.  
  554.         // style index changed?
  555.         if (oldStyleIndex != newStyleIndex)
  556.         {
  557.             // if offset is in the middle of a style run, insert a new style run in the run array
  558.             if (offset > runInfo.runStart)
  559.             {
  560.                 if ((err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE)) != noErr)
  561.                 {
  562.                     goto cleanup;
  563.                 }
  564.                 runIndex++;
  565.             }
  566.             else
  567.             {
  568.                 // otherwise just change the styleIndex field of the current style run element
  569.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  570.             }
  571.  
  572.             // if specified range ends in the middle of a style run, insert yet another element
  573.             if (rangeEnd < runInfo.runEnd)
  574.             {
  575.                 if ((err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE)) != noErr)
  576.                 {
  577.                     goto cleanup;
  578.                 }
  579.             }
  580.         } // if oldStyle != newStyle
  581.  
  582.         // go to next style run
  583.         runIndex++;
  584.         offset = runInfo.runEnd;
  585.  
  586.     } while (offset < rangeEnd);
  587.  
  588.     // if the last style run ends exactly at the end of the specified range,
  589.     // see if we can merge it with the following style run
  590.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) &&
  591.         ((*hRuns)[runIndex].styleIndex == newStyleIndex))
  592.     {
  593.         _WERemoveRun(runIndex, pWE);
  594.     }
  595.  
  596.     // clear result code
  597.     err = noErr;
  598.  
  599. cleanup:
  600.     // return result code
  601.     return err;
  602. }
  603.  
  604. pascal OSErr _WEApplyStyleScrap(SInt32 rangeStart, SInt32 rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  605. {
  606.     // apply the given style scrap to the specified text range
  607.  
  608.     WEPtr pWE = *hWE;    // assume WE record is already locked
  609.     TEStyleScrapElement *pElement;
  610.     Size styleScrapSize;
  611.     SInt32 runStart, runEnd;
  612.     SInt32 entryCount, index;
  613.     WETextStyle ts;
  614.     OSErr err;
  615.  
  616.     // sanity check: make sure the style scrap handle is a reasonable size
  617.     err = weCorruptDataErr;
  618.     styleScrapSize = GetHandleSize((Handle) styleScrap) - sizeof(SInt16);
  619.     if (styleScrapSize < 0)
  620.     {
  621.         goto cleanup;        // too short: header is missing
  622.     }
  623.  
  624.     // calculate entry count based on handle size
  625.     entryCount = styleScrapSize / sizeof(ScrpSTElement);
  626.     if (styleScrapSize != entryCount * sizeof(ScrpSTElement))
  627.     {
  628.         goto cleanup;        // not an integral number of entries
  629.     }
  630.  
  631.     // the calculated entry count must match scrpNStyles,
  632.     // unless it's larger than 32,766
  633.     if (entryCount < SHRT_MAX)
  634.     {
  635.         if (entryCount != (*styleScrap)->scrpNStyles)
  636.         {
  637.             goto cleanup;    // invalid entry count
  638.         }
  639.     }
  640.  
  641.     // loop through each element of the style scrap
  642.     for ( index = 0; index < entryCount; index++ )
  643.     {
  644.         // get a pointer to the current scrap element
  645.         pElement = (TEStyleScrapElement *) ((*styleScrap)->scrpStyleTab + index);
  646.  
  647.         // calculate text run to which this element is to be applied
  648.         runStart = rangeStart + pElement->scrpStartChar;
  649.         if (index < entryCount - 1)
  650.         {
  651.             runEnd = rangeStart + pElement[1].scrpStartChar;
  652.         }
  653.         else
  654.         {
  655.             runEnd = rangeEnd;
  656.         }
  657.  
  658.         // perform some range checking
  659.         if (runEnd > rangeEnd)
  660.         {
  661.             runEnd = rangeEnd;
  662.         }
  663.         if (runStart >= runEnd)
  664.         {
  665.             continue;
  666.         }
  667.  
  668.         // copy style to a local variable in case memory moves
  669.         * (TextStyle *) &ts = pElement->scrpTEAttrs.runTEStyle;
  670.  
  671.         // apply the specified style to the range
  672.         if ((err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE)) != noErr)
  673.         {
  674.             goto cleanup;
  675.         }
  676.     }
  677.  
  678.     // clear result code
  679.     err = noErr;
  680.  
  681. cleanup:
  682.     // return result code
  683.     return err;
  684. }
  685.  
  686. #if WASTE_OBJECTS
  687.  
  688. pascal OSErr _WEApplySoup(SInt32 offset, Handle hSoup, WEHandle hWE)
  689. {
  690.     WESoup soup;
  691.     Ptr pSoup, pSoupEnd;
  692.     WETextStyle ts;
  693.     Handle hObjectData;
  694.     SInt32 objectOffset;
  695.     Boolean saveWELock;
  696.     OSErr err;
  697.  
  698.     BLOCK_CLR(ts);
  699.     hObjectData = nil;
  700.  
  701.     // lock the WE record
  702.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  703.  
  704.     // lock the soup in high heap
  705.     HLockHi(hSoup);
  706.     pSoup = *hSoup;
  707.     pSoupEnd = pSoup + GetHandleSize(hSoup);
  708.  
  709.     // loop through each object descriptor in the soup
  710.     while (pSoup < pSoupEnd)
  711.     {
  712.         // Object descriptors may be aligned to odd addresses (duh!)
  713.         // this isn't a problem on 68020+ and PPC CPUs, but causes
  714.         // a fatal address error on the 68000.  To avoid this, we
  715.         // copy the descriptor to the stack with BlockMoveData()
  716.         // before trying to access its fields.
  717.         BlockMoveData(pSoup, &soup, sizeof(soup));
  718.  
  719.         // if soupDataSize is negative, this soup is a special type that we won't handle here
  720.         if (soup.soupDataSize < 0)
  721.             continue;
  722.  
  723.         // create a new relocatable block the hold the object data
  724.         if ((err = _WEAllocate(soup.soupDataSize, kAllocTemp, &hObjectData)) != noErr)
  725.             goto cleanup;
  726.  
  727.         // copy the object data to this block
  728.         BlockMoveData(pSoup + sizeof(soup), *hObjectData, soup.soupDataSize);
  729.  
  730.         // create a new object out of the tagged data
  731.         if ((err = _WENewObject(soup.soupType, hObjectData, hWE, &ts.tsObject)) != noErr)
  732.             goto cleanup;
  733.  
  734.         // if there was no new handler for this object, use the object size stored in the soup
  735.         if ((*ts.tsObject)->objectTable == nil)
  736.             (*ts.tsObject)->objectSize = soup.soupSize;
  737.  
  738.         // record a reference to the object descriptor in the style table
  739.         objectOffset = soup.soupOffset + offset;
  740.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  741.         hObjectData = nil;
  742.         ts.tsObject = nil;
  743.         if (err != noErr)
  744.             goto cleanup;
  745.  
  746.         // advance soup pointer
  747.         pSoup += sizeof(soup) + soup.soupDataSize;
  748.  
  749.     } // while
  750.  
  751.     // clear result code
  752.     err = noErr;
  753.  
  754. cleanup:
  755.     // clean up
  756.     HUnlock(hSoup);
  757.     _WEForgetHandle((Handle *) &ts.tsObject);
  758.     _WEForgetHandle(&hObjectData);
  759.  
  760.     // unlock the WE record
  761.     _WESetHandleLock((Handle) hWE, saveWELock);
  762.  
  763.     // return result code
  764.     return err;
  765. }
  766.  
  767. #endif    // WASTE_OBJECTS
  768.  
  769. pascal void _WEBumpRunStart(SInt32 runIndex, SInt32 deltaRunStart, WEPtr pWE)
  770. {
  771.     // add deltaLineStart to the lineStart field of all line records
  772.     // starting from lineIndex
  773.  
  774.     RunArrayElementPtr pRun = *pWE->hRuns + runIndex;
  775.     SInt32 nRuns = pWE->nRuns;
  776.  
  777.     // loop through the style run array adjusting the runStart fields
  778.     for ( ; runIndex <= nRuns; runIndex++ )
  779.     {
  780.         pRun->runStart += deltaRunStart;
  781.         pRun++;
  782.     }
  783. }
  784.  
  785. pascal void _WERemoveRunRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  786. {
  787.     // the range of text between rangeStart and rangeEnd is being deleted
  788.     // update the style run array (and the style table) accordingly
  789.  
  790.     WEPtr pWE = *hWE;    // assume WE record is already locked
  791.     RunArrayElementPtr pRun;
  792.     SInt32 startRun, endRun;
  793.  
  794.     // find the index to the first and last style runs in the specified range
  795.     startRun = WEOffsetToRun(rangeStart, hWE);
  796.     endRun = WEOffsetToRun(rangeEnd, hWE) - 1;
  797.  
  798.     // remove all style runs between startRun and endRun
  799.     for ( ; endRun > startRun; endRun-- )
  800.         _WERemoveRun(endRun, pWE);
  801.  
  802.     // move back all subsequent style runs
  803.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  804.  
  805.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  806.     {
  807.         pRun = *pWE->hRuns + endRun;
  808.         pRun[1].runStart = rangeStart;
  809.     }
  810.  
  811.     // remove the first style run if is has become zero length
  812.     pRun = *pWE->hRuns + startRun;
  813.     if (pRun[0].runStart == pRun[1].runStart)
  814.     {
  815.         _WERemoveRun(startRun, pWE);
  816.         startRun--;
  817.     }
  818.  
  819.     // merge the first and last runs if they have the same style index
  820.     if (startRun >= 0)
  821.     {
  822.         pRun = *pWE->hRuns + startRun;
  823.         if (pRun[0].styleIndex == pRun[1].styleIndex)
  824.         {
  825.             _WERemoveRun(startRun + 1, pWE);
  826.         }
  827.     }
  828. }
  829.  
  830. pascal void _WEBumpLineStart(SInt32 lineIndex, SInt32 deltaLineStart, WEPtr pWE)
  831. {
  832.     // add deltaLineStart to the lineStart field of all line records
  833.     // starting from lineIndex
  834.  
  835.     LineRec *pLine = *pWE->hLines + lineIndex;
  836.     SInt32 nLines = pWE->nLines;
  837.  
  838.     // loop through the line array adjusting the lineStart fields
  839.     for ( ; lineIndex <= nLines; lineIndex++ )
  840.     {
  841.         pLine->lineStart += deltaLineStart;
  842.         pLine++;
  843.     }
  844. }
  845.  
  846. pascal void _WERemoveLineRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  847. {
  848.     // the range of text between rangeStart and rangeEnd is being deleted
  849.     // update the line array accordingly
  850.  
  851.     WEPtr pWE = *hWE;    // assume WE record is already locked
  852.     SInt32 startLine, nLines;
  853.  
  854.     // remove all line records between rangeStart and rangeEnd
  855.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  856.     nLines = WEOffsetToLine(rangeEnd, hWE) - startLine + 1;
  857.  
  858.     _WESplice((Handle) pWE->hLines, nil, - (nLines * sizeof(LineRec)), startLine * sizeof(LineRec));
  859.     pWE->nLines -= nLines;
  860.  
  861.     // update the lineStart field of all the line records that follow
  862.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  863. }
  864.  
  865. pascal OSErr _WEDeleteRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  866. {
  867.     // used internally to delete a text range
  868.     WEPtr pWE = *hWE;    // assume WE record is already locked
  869.     WERunInfo runInfo;
  870.     OSErr err = noErr;
  871.  
  872.     if (rangeEnd > pWE->textLength)
  873.         rangeEnd = pWE->textLength;
  874.  
  875.     // do nothing if the specified range is empty
  876.     if (rangeStart == rangeEnd)
  877.         goto cleanup;
  878.  
  879.     // save the first style in the specified range in nullStyle
  880.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  881.     pWE->nullStyle = runInfo.runAttrs;
  882.     BSET(pWE->flags, weFUseNullStyle);
  883.  
  884. #if WASTE_OBJECTS
  885.     // special case: if we're deleting up to the end of the text, see whether
  886.     // there's an embedded object at the very end and remove it
  887.     if (rangeEnd == pWE->textLength)
  888.     {
  889.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  890.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  891.         {
  892.             runInfo.runAttrs.runStyle.tsObject = nil;
  893.             if ((err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE)) != noErr)
  894.                 goto cleanup;
  895.         }
  896.     }
  897. #endif
  898.  
  899.     // remove all line records between rangeStart and rangeEnd
  900.     _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  901.  
  902.     // remove all style runs between rangeStart and rangeEnd
  903.     _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  904.  
  905.     // remove the text
  906.     if ((err = _WESplice(pWE->hText, nil, rangeStart - rangeEnd, rangeStart)) != noErr)
  907.         goto cleanup;
  908.  
  909.     // update textLength field
  910.     pWE->textLength -= (rangeEnd - rangeStart);
  911.  
  912.     // we modified the text, so the anchor range (if any) is no longer valid
  913.     pWE->clickCount = 0;
  914.  
  915. cleanup:
  916.     // return result code
  917.     return err;
  918. }
  919.  
  920. pascal OSErr _WEInsertText(SInt32 offset, Ptr textPtr, SInt32 textLength, WEHandle hWE)
  921. {
  922.     WEPtr pWE = *hWE;    // assume WE record is already locked
  923.     WEStyleMode mode;
  924.     OSErr err = noErr;
  925.  
  926.     // do nothing if textLength is zero or negative
  927.     if (textLength <= 0)
  928.         goto cleanup;
  929.  
  930.     // insert the text
  931.     if ((err = _WESplice(pWE->hText, textPtr, textLength, offset)) != noErr)
  932.         goto cleanup;
  933.  
  934.     // update the lineStart fields of all lines following the insertion point
  935.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  936.  
  937.     // update the runStart fields of all style runs following the insertion point
  938.     _WEBumpRunStart(WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  939.  
  940.     // update the textLength field
  941.     pWE->textLength += textLength;
  942.  
  943.     // we modified the text, so the anchor range (if any) is no longer valid
  944.     pWE->clickCount = 0;
  945.  
  946.     // make sure the newly inserted text doesn't reference any embedded object
  947. #if WASTE_OBJECTS
  948.     pWE->nullStyle.runStyle.tsObject = nil;
  949. #endif
  950.     mode = weDoObject;
  951.  
  952.     // if there is a valid null style, apply it to the newly inserted text
  953.     if (BTST(pWE->flags, weFUseNullStyle))
  954.     {
  955.         mode += (weDoAll + weDoReplaceFace);
  956.     }
  957.  
  958.     if ((err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE)) != noErr)
  959.         goto cleanup;
  960.  
  961. cleanup:
  962.     return err;
  963. }
  964.